package zhongbo.java.air.auth.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import zhongbo.java.air.auth.model.User;
import zhongbo.java.air.auth.service.IAuthService;
import zhongbo.java.air.auth.service.IUserService;
import zhongbo.java.air.auth.vo.AuthResponseVo;
import zhongbo.java.air.common.util.JwtUtil;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 说明：
 *
 * @author 钟波dellNotebook
 * @since 2025年03月29日 星期六 13:39:44
 */
@Service
// 指定多个bean实现中的主类
//@Primary
@Slf4j
public class AuthServiceImpl implements IAuthService {

    /**
     * token缓存key
     */
    private static final String CACHE_TOKEN_KEY = "token:";

    @Autowired
    private IUserService userService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public AuthResponseVo register(String username, String password) {
        Assert.notBlank(username, "用户名不能为空");
        Assert.notBlank(password, "用户密码不能为空");
        // 检查用户名是否已存在
        User find = userService.findByUserName(username);
        if (find != null) {
            throw new RuntimeException("用户名已存在");
        }
        User user = new User();
        user.setUserName(username);
        user.setPassWord(password);
        User add = userService.add(user);
        return generateAuthResponse(add);
    }

    private AuthResponseVo generateAuthResponse(User user) {
        String userName = user.getUserName();
        Map<String, Object> toMap = BeanUtil.beanToMap(user);
        String token = JwtUtil.createToken(userName, toMap);
        redisTemplate.opsForValue().set(tokenKey(token), token);
        redisTemplate.expire(tokenKey(token), JwtUtil.tokenExpireTime, TimeUnit.MILLISECONDS);
        return getTokenInfo(token);
    }

    private String tokenKey(String token) {
        return CACHE_TOKEN_KEY + token;
    }

    @Override
    public AuthResponseVo login(String username, String password) {
        User find = userService.findByUserNameAndPassWord(username, password);
        if (find == null) {
            throw new RuntimeException("用户名或密码错误");
        }
        return generateAuthResponse(find);
    }

    @Override
    public void logout(String token) {
        if (StrUtil.isNotBlank(token)) {
            redisTemplate.delete(tokenKey(token));
        }
    }

    @Override
    public boolean validateToken(String token) {
        if (StrUtil.isBlank(token)) {
            return false;
        }
        try {
            String tokenRedis = redisTemplate.opsForValue().get(tokenKey(token));
            AuthResponseVo tokenInfo = getTokenInfo(tokenRedis);
            Long expireTime = tokenInfo.getExpireTime();
            return System.currentTimeMillis() < expireTime;
        } catch (Exception e) {
            log.error("token验证失败 {}", e.getMessage());
        }
        return false;
    }

    @Override
    public AuthResponseVo getTokenInfo(String token) {
        if (StrUtil.isBlank(token)) {
            throw new RuntimeException("token不能为空");
        }
        Jws<Claims> claimsJws = JwtUtil.parseToken(token);
        Claims body = claimsJws.getBody();
        User user = BeanUtil.toBean(body, User.class);
        long expireTime = claimsJws.getBody().getExpiration().getTime();
        AuthResponseVo authResponseVo = new AuthResponseVo();
        authResponseVo.setToken(token);
        authResponseVo.setUser(user);
        authResponseVo.setExpireTime(expireTime);
        return authResponseVo;
    }

    @Override
    public AuthResponseVo refreshToken(String token) {
        AuthResponseVo tokenInfo = getTokenInfo(token);
        User user = tokenInfo.getUser();
        // 删除旧的token
        // 安全性高: 删除旧的; 用户体验好: 保留旧的;
        // redisTemplate.delete(tokenKey(token));
        return generateAuthResponse(user);
    }

}
